use super::{SpiDriverPin, EPD_2IN13_FULL, EPD_2IN13_PART};
use crate::e_paper::{HEIGHT, WIDTH};
use std::thread::sleep;
use std::time::Duration;

pub const EPD_2IN13_LUT_FULL_UPDATE: &[u8] = &[
    0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
];

pub const EPD_2IN13_LUT_PARTIAL_UPDATE: &[u8] = &[
    0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];

pub struct SpiContextV1<T: SpiDriverPin> {
    pin: T,
}
impl<T: SpiDriverPin> SpiContextV1<T> {
    pub fn new(pin: T) -> Self {
        Self { pin }
    }
    pub fn config(&mut self) -> anyhow::Result<()> {
        println!("config");
        self.pin.set_cs(true)?;
        self.pin.set_sck(false)?;
        Ok(())
    }
    pub fn reset(&mut self) -> anyhow::Result<()> {
        println!("reset");
        self.pin.set_rst(true)?;
        sleep(Duration::from_millis(200));
        self.pin.set_rst(false)?;
        sleep(Duration::from_millis(10));
        self.pin.set_rst(true)?;
        sleep(Duration::from_millis(200));
        Ok(())
    }
    pub fn write_byte(&mut self, data: u8) -> anyhow::Result<()> {
        // println!("write_byte");
        self.pin.set_cs(false)?;
        let mut d = data;
        for _i in 0..8 {
            if d & 0x80 == 0 {
                self.pin.set_sdo(false)?;
            } else {
                self.pin.set_sdo(true)?;
            }
            d <<= 1;
            self.pin.set_sck(true)?;
            self.pin.set_sck(false)?;
        }
        self.pin.set_cs(true)?;
        Ok(())
    }
    pub fn write_bytes(&mut self, data: &[u8]) -> anyhow::Result<()> {
        for v in data {
            self.write_byte(*v)?;
        }
        Ok(())
    }
    pub fn send_cmd(&mut self, data: u8) -> anyhow::Result<()> {
        self.send_data_internal(data, true)
    }
    pub fn send_data(&mut self, data: u8) -> anyhow::Result<()> {
        self.send_data_internal(data, false)
    }
    fn send_data_internal(&mut self, data: u8, is_cmd: bool) -> anyhow::Result<()> {
        if is_cmd {
            self.pin.set_dc(false)?;
        } else {
            self.pin.set_dc(true)?;
        }
        self.pin.set_cs(false)?;
        self.write_byte(data)?;
        self.pin.set_cs(true)?;
        Ok(())
    }
    pub fn init(&mut self, mode: u8) -> anyhow::Result<()> {
        self.config()?;

        self.reset()?;

        println!("DRIVER_OUTPUT_CONTROL");
        self.send_cmd(0x1)?;
        self.send_data((HEIGHT - 1) as u8 & 0xFF)?;
        self.send_data(((HEIGHT - 1) >> 8) as u8 & 0xFF)?;
        self.send_data(0x00)?; // GD = 0; SM = 0; TB = 0;

        println!("BOOSTER_SOFT_START_CONTROL");
        self.send_cmd(0x0c)?;
        self.send_data(0xd7)?;
        self.send_data(0xd6)?;
        self.send_data(0x9d)?;

        println!("WRITE_VCOM_REGISTER");
        self.send_cmd(0x2C)?;
        self.send_data(0xA8)?; //VCOM 7C

        println!("SET_DUMMY_LINE_PERIOD");
        self.send_cmd(0x3A)?;
        self.send_data(0x1A)?; //4 dummy lines per gate

        println!("SET_GATE_TIME");
        self.send_cmd(0x3B)?;
        self.send_data(0x08)?; // 2us per line

        println!("BORDER_WAVEFORM_CONTROL");
        self.send_cmd(0x3C)?;
        self.send_data(0x63)?;

        println!("DATA_ENTRY_MODE_SETTING");
        self.send_cmd(0x11)?;
        self.send_data(0x03)?; // X increment; Y increment

        //set the look-up table register
        self.send_cmd(0x32)?;
        if mode == EPD_2IN13_FULL {
            for i in 0..EPD_2IN13_LUT_FULL_UPDATE.len() {
                self.send_data(EPD_2IN13_LUT_FULL_UPDATE[i])?;
            }
        } else if mode == EPD_2IN13_PART {
            for i in 0..EPD_2IN13_LUT_PARTIAL_UPDATE.len() {
                self.send_data(EPD_2IN13_LUT_PARTIAL_UPDATE[i])?;
            }
        } else {
            println!("invalid mode {}", mode);
        }

        Ok(())
    }

    pub fn wait_ready(&mut self) {
        while self.pin.busy() {
            sleep(Duration::from_millis(100));
        }
    }

    pub fn turnon_display(&mut self) -> anyhow::Result<()> {
        println!("DISPLAY_UPDATE_CONTROL_2");
        self.send_cmd(0x22)?;
        self.send_data(0xC4)?;
        self.send_cmd(0x20)?; //MASTER_ACTIVATION
        self.send_cmd(0xFF)?; //TERMINATE_FRAME_READ_WRITE
        self.wait_ready();
        Ok(())
    }

    pub fn set_window(
        &mut self,
        x_start: u16,
        y_start: u16,
        x_end: u16,
        y_end: u16,
    ) -> anyhow::Result<()> {
        println!("set window {}x{} {}x{}", x_start, y_start, x_end, y_end);
        self.send_cmd(0x44)?;
        /* x point must be the multiple of 8 or the last 3 bits will be ignored */
        self.send_data((x_start >> 3) as u8 & 0xFF)?;
        self.send_data((x_end >> 3) as u8 & 0xFF)?;

        self.send_cmd(0x45)?;
        self.send_data(y_start as u8 & 0xFF)?;
        self.send_data((y_start >> 8) as u8 & 0xFF)?;
        self.send_data(y_end as u8 & 0xFF)?;
        self.send_data((y_end >> 8) as u8 & 0xFF)?;
        Ok(())
    }
    pub fn set_cursor(&mut self, x: u16, y: u16) -> anyhow::Result<()> {
        // println!("set cursor {}x{}", x, y);
        self.send_cmd(0x4E)?;
        /* x point must be the multiple of 8 or the last 3 bits will be ignored */
        self.send_data((x >> 3) as u8 & 0xFF)?;

        self.send_cmd(0x4F)?;
        self.send_data(y as u8 & 0xFF)?;
        self.send_data((y >> 8) as u8 & 0xFF)?;
        Ok(())
    }
    pub fn sleep(&mut self) -> anyhow::Result<()> {
        println!("sleep mode");
        self.send_cmd(0x10)?; //DEEP_SLEEP_MODE
        self.send_data(0x1)?;
        Ok(())
    }

    pub fn clear(&mut self) -> anyhow::Result<()> {
        println!("clear display");
        let width = if WIDTH % 8 == 0 {
            WIDTH / 8
        } else {
            WIDTH / 8 + 1
        };
        let height = HEIGHT;
        self.set_window(0, 0, WIDTH, HEIGHT)?;
        for j in 0..height {
            self.set_cursor(0, j)?;
            self.send_cmd(0x24)?;
            for _i in 0..width {
                self.send_data(0xff)?;
            }
        }
        self.turnon_display()?;
        Ok(())
    }
    pub fn display(&mut self, data: &[u8]) -> anyhow::Result<()> {
        println!("show image len {}", data.len());
        let width = if WIDTH % 8 == 0 {
            WIDTH / 8
        } else {
            WIDTH / 8 + 1
        };
        let height = HEIGHT;
        self.set_window(0, 0, WIDTH, HEIGHT)?;
        for j in 0..height {
            self.set_cursor(0, j)?;
            self.send_cmd(0x24)?;
            for i in 0..width {
                let index = (i + j * width) as usize;
                if index >= data.len() {
                    continue;
                }
                self.send_data(data[index as usize])?;
            }
        }
        self.turnon_display()?;
        Ok(())
    }
    pub fn test(&mut self) -> anyhow::Result<()> {
        println!("test>>>");
        // let mut enable = false;
        // loop {
        //     enable = if enable { false } else { true };
        //     if enable {
        //         self.cs.set_high()?;
        //     } else {
        //         self.cs.set_low()?;
        //     }
        //     sleep(Duration::from_millis(50));
        // }
        println!("test<<<");
        Ok(())
    }
}
